6.5 通道
原子函数和互斥锁都能工作,但是依靠它们都不会让编写并发程序变得更简单,更不容易出错,或者更有趣。在Go语言里,你不仅可以使用原子函数和互斥锁来保证对共享资源的安全访问以及消除竞争状态,还可以使用通道,通过发送和接收需要共享的资源,在goroutine之间做同步。
当一个资源需要在goroutine之间共享时,通道在goroutine之间架起了一个管道,并提供了确保同步交换数据的机制。声明通道时,需要指定将要被共享的数据的类型。可以通过通道共享内置类型、命名类型、结构类型和引用类型的值或者指针。
在Go语言中需要使用内置函数 make
来创建一个通道,如代码清单6-17所示。
代码清单6-17 使用 make
创建通道
// 无缓冲的整型通道
unbuffered := make(chan int)
// 有缓冲的字符串通道
buffered := make(chan string, 10)
在代码清单6-17中,可以看到使用内置函数 make
创建了两个通道,一个无缓冲的通道,一个有缓冲的通道。 make
的第一个参数需要是关键字 chan
,之后跟着允许通道交换的数据的类型。如果创建的是一个有缓冲的通道,之后还需要在第二个参数指定这个通道的缓冲区的大小。
向通道发送值或者指针需要用到 <-
操作符,如代码清单6-18所示。
代码清单6-18 向通道发送值
// 有缓冲的字符串通道
buffered := make(chan string, 10)
// 通过通道发送一个字符串
buffered <- "Gopher"
在代码清单6-18里,我们创建了一个有缓冲的通道,数据类型是字符串,包含一个10个值的缓冲区。之后我们通过通道发送字符串 "Gopher"
。为了让另一个goroutine可以从该通道里接收到这个字符串,我们依旧使用 <-
操作符,但这次是一元运算符,如代码清单6-19所示。
代码清单6-19 从通道里接收值
// 从通道接收一个字符串
value := <-buffered
当从通道里接收一个值或者指针时, <-
运算符在要操作的通道变量的左侧,如代码清单6-19所示。
通道是否带有缓冲,其行为会有一些不同。理解这个差异对决定到底应该使用还是不使用缓冲很有帮助。下面我们分别介绍一下这两种类型。